package com.naiterui.ehp.bs.recommend.service.impl;

import com.naiterui.common.redis.RedisUtil;
import com.naiterui.ehp.bp.bo.doctor.RecomDoctorBO;
import com.naiterui.ehp.bp.bo.doctor.RecomUserInfoBO;
import com.naiterui.ehp.bp.bo.recommend.RecomDiagnosisBO;
import com.naiterui.ehp.bp.bo.recommend.SuperviseDiagnosisBO;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.support.exception.ResponseVO;
import com.naiterui.ehp.bp.utils.lang.ConfigUtil;
import com.naiterui.ehp.bp.utils.sort.ABCUtil;
import com.naiterui.ehp.bp.utils.sort.ABCUtil.ABCVO;
import com.naiterui.ehp.bs.recommend.bean.DrStandardDiagnosis;
import com.naiterui.ehp.bs.recommend.exception.ExceptionCodes;
import com.naiterui.ehp.bs.recommend.remote.IRecommendRemoter;
import com.naiterui.ehp.bs.recommend.repository.mysql.IDrStandardDiagnosisRepository;
import com.naiterui.ehp.bs.recommend.service.IDrDiagnosisService;
import com.naiterui.ehp.bs.recommend.vo.param.CommonDiagnosisVO;
import com.naiterui.ehp.bs.recommend.vo.param.DiagnosisVO;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import cn.hutool.core.collection.CollectionUtil;
import java.io.InputStream;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;

@Service
public class DrDiagnosisServiceImpl implements IDrDiagnosisService {

    private static final Logger LOGGER = LoggerFactory.getLogger(DrDiagnosisServiceImpl.class);

    @Autowired
    private IDrStandardDiagnosisRepository drStandardDiagnosisRepository;
    @Autowired
    private IRecommendRemoter remoter;

    private static final Integer TOP_100 = 100;


    private static String RECOM_DELETE_DIAGNOSE_TIP = ConfigUtil.getString("recom.delete.diagnose.tip");


    /**
     * 查询指定id列表的标准诊断
     *
     * @param idSet
     *
     * @return
     */
    public List<DrStandardDiagnosis> findStandardByIds(Set<Long> idSet) {
        return drStandardDiagnosisRepository.findByIdIn(idSet);
    }

    /**
     * 解析excel文件
     *
     * @param wb
     *
     * @return
     */

    private List<DrStandardDiagnosis> parseWorkbook(Workbook wb) {
        Sheet sheet = wb.getSheetAt(0);
        // 获取文件的总行数(长度)
        int rowLength = sheet.getLastRowNum();

        List<DrStandardDiagnosis> list = new ArrayList<>(10000);
        // 迭代文件，读取文件内容
        for (int rowNum = 2; rowNum <= rowLength; rowNum++) {
            Row row = sheet.getRow(rowNum);
            Cell mainCodeCell = row.getCell(0);
            Cell attchCodeCell = row.getCell(1);
            Cell nameCell = row.getCell(2);
            Cell aliasCell = row.getCell(3);
            String mainCode = mainCodeCell == null ? "" : mainCodeCell.getStringCellValue().trim();
            String attchCode = attchCodeCell == null ? "" : attchCodeCell.getStringCellValue().trim();
            String name = nameCell == null ? "" : nameCell.getStringCellValue().trim();
            String alias = aliasCell == null ? "" : aliasCell.getStringCellValue().trim();
            list.add(new DrStandardDiagnosis(mainCode, attchCode, name, alias));
        }
        return list;
    }

    /**
     * 保存医生自定义诊断(推荐用药时候)
     * DrDiagnosisServiceImpl.saveCommmonDiagnosis()
     *
     * @param doctorId
     * @param nameList
     *
     * @return
     *
     * @throws BusinessException
     * @Author chenlin
     * @Date 2017年8月16日
     * @since 1.0.0
     */

    private List<DiagnosisVO> saveCustomDiagnosis(Long doctorId, List<String> nameList) throws BusinessException {

        List<DiagnosisVO> customList = new ArrayList<>();
        for (String tempName : nameList) {
            if (StringUtils.isEmpty(tempName)) {
                LOGGER.error("save custom diagnosis doctorId {} and name {} is empty ", doctorId, tempName);
                continue;
            }
            String name = tempName.trim();
            if (name.length() > 40) {
                LOGGER.error("save custom diagnosis doctorId {} and name {} is more length ", doctorId, name);
                throw new BusinessException(ExceptionCodes.INPUT_TOO_LONG);
            }
            // 查询诊断名称是否存在于标准诊断中，如果存在直接返回标准库id即可
            DrStandardDiagnosis tempDiagnosis = drStandardDiagnosisRepository.findFirstByName(name);
            long id = tempDiagnosis == null ? -1 : tempDiagnosis.getId();
            if (id > 0) {
                LOGGER.info("save custom diagnosis doctorId {} and name {} is standrad already exists ", doctorId, name);
                DiagnosisVO diagnosis = new DiagnosisVO(id, name, DiagnosisVO.TYPE_STANDARD);
                customList.add(diagnosis);
                continue;
            }
        }
        return customList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveImportFile(MultipartFile file) throws Exception {
        if (file == null) {
            LOGGER.error("file is null");
            return;
        }

        // 读取文件到workbook,并关闭文件流
        LOGGER.info("start read file ...");
        InputStream is = file.getInputStream();
        Workbook wb = WorkbookFactory.create(is);
        is.close();

        // 解析workbook内容为实体
        LOGGER.info("parse file to list ...");
        List<DrStandardDiagnosis> list = parseWorkbook(wb);

        // 保存标准诊断
        LOGGER.info("start save list to db ...");
        saveStandardDiagnosis(list);
        LOGGER.info("end save list to db ...");

    }

    private void saveStandardDiagnosis(List<DrStandardDiagnosis> list) {

        // 判断集合是否为空
        if (list == null || list.isEmpty()) {
            LOGGER.error("parse file to list is empty");
            return;
        }

        for (DrStandardDiagnosis diagnosis : list) {
            String name = diagnosis.getName();
            // 如果名称为空，则跳过该条记录
            if (StringUtils.isEmpty(name)) {
                LOGGER.error("inline the diagnosis name is empty");
                continue;
            }

            // 判断名称是在标准库中已经存在
            DrStandardDiagnosis tempDiagnosis = drStandardDiagnosisRepository.findFirstByName(name);
            long id = tempDiagnosis == null ? -1 : tempDiagnosis.getId();
            if (id > 0) {
                LOGGER.error("inline the diagnosis name {} is already exsits", name);
                continue;
            }

            // 保存到标准库
            drStandardDiagnosisRepository.save(diagnosis);
        }

    }


    @Override
    public List<DiagnosisVO> updateCommonDiagnosis(Long doctorId, List<String> diagnosisList) throws BusinessException {
        // 1 从标准诊断中查询
        Set<Long> standardIds = new HashSet<>();
        List<DiagnosisVO> resultList = new ArrayList<>();
        List<DrStandardDiagnosis> standardList = drStandardDiagnosisRepository.findByNameIn(diagnosisList);
        for (DrStandardDiagnosis diagnosis : standardList) {
            String name = diagnosis.getName();
            if (diagnosisList.remove(name)) {
                DiagnosisVO vo = new DiagnosisVO(diagnosis.getId(), diagnosis.getName(), DiagnosisVO.TYPE_STANDARD);
                resultList.add(vo);
                standardIds.add(diagnosis.getId());
            }
        }

        if (CollectionUtils.isNotEmpty(diagnosisList)) {
            //如果是灰度诊断医生,则过滤掉非标准诊断
            RecomUserInfoBO userInfo = remoter.getUserInfo(doctorId, null, true);
            if (null == userInfo || null == userInfo.getDoctor()) {
                LOGGER.warn("[更新诊断] updateCommonDiagnosis error ; 用户信息异常 : doctorId={}", doctorId);
                throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_EXITS);
            }
            RecomDoctorBO doctorBO = userInfo.getDoctor();
            if (doctorBO.getLabel() != null && doctorBO.getLabel().getDiagnosis()) {
                diagnosisList.clear();
            }
        }

        // 3.保存剩余的诊断为自定义诊断
        List<DiagnosisVO> list = saveCustomDiagnosis(doctorId, diagnosisList);
        resultList.addAll(list);
        for (DiagnosisVO vo : list) {
            Long id = vo.getId();
            if (vo.getType() == DiagnosisVO.TYPE_STANDARD) {
                standardIds.add(id);
            }
        }

        // 4.计算出自定义+标准的诊断，将其标识为id+type的字符串
        Set<String> diagnosisRedisValue = new HashSet<>();
        addDiagnosisRedisKey(diagnosisRedisValue, DiagnosisVO.TYPE_STANDARD, standardIds);

        // 5 更新redis中常用诊断数据
        Map<String, Double> diagnosisScoreMap = new HashMap<>();
        double now = (double) System.currentTimeMillis();
        for (String idType : diagnosisRedisValue) {
            diagnosisScoreMap.put(idType, now);
        }
        if (!diagnosisScoreMap.isEmpty()) {
            String key = getTopDiagnosisKey(doctorId);
            RedisUtil.zsetOps().zadd(getTopDiagnosisKey(doctorId), diagnosisScoreMap);
            Long mapSize = RedisUtil.zsetOps().zcard(key);
            //超过100个的值删除
            //修改原有逻辑bug
            if (mapSize > TOP_100) {
                Long stop = mapSize - 100;
                //按照排名范围删除元素，按照分数从小到大的顺序删除所指定的排名范围内的所有元素
                RedisUtil.zsetOps().zremrangeByRank(key, 0, stop - 1);
            }
        }

        return resultList;
    }

    private void addDiagnosisRedisKey(Set<String> diagnosisRedisValue, int type, Set<Long> diagnosisId) {
        for (Long id : diagnosisId) {
            diagnosisRedisValue.add(id + "-" + type);
        }
    }


    private String getTopDiagnosisKey(Long doctorId) {
        return CommonConstant.REDIS_PRE_DR + "top_diagnosis_" + doctorId;
    }

    /**
     * 找到常用诊断列表
     * DrDiagnosisServiceImpl.findCommonDiagnosis()
     *
     * @param doctorId
     *
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws NoSuchFieldException
     * @throws SecurityException
     * @Author chenlin
     * @Date 2017年7月31日
     * @since 1.0.0
     */

    @Override
    @Transactional
    public CommonDiagnosisVO findCommonDiagnosis(Long doctorId) throws Exception {
        CommonDiagnosisVO commonDiagnosisVO = new CommonDiagnosisVO();
        commonDiagnosisVO.setStandard(CommonDiagnosisVO.STANDARD_NO);
        // 1 迭代处理诊断，帅选出自定义诊断
        String redisKey = getTopDiagnosisKey(doctorId);
        Set<String> disanosisSet = RedisUtil.zsetOps().zrevrange(redisKey, 0, 99);
        Set<Long> customDiag = new HashSet<>();
        Set<Long> standardDiag = new HashSet<>();
        Map<String, DiagnosisVO> map = new LinkedHashMap<>();
        for (String idType : disanosisSet) {
            String[] idTypeArr = idType.split("-");
            map.put(idType, null);
            if (idTypeArr.length == 2) {
                Long id = Long.parseLong(idTypeArr[0]);
                int type = Integer.parseInt(idTypeArr[1]);
                if (type == DiagnosisVO.TYPE_CUSTOM) {
                    customDiag.add(id);
                } else if (type == DiagnosisVO.TYPE_STANDARD) {
                    standardDiag.add(id);
                }
            }
        }
        //医生端3.5  灰度诊断医生删除非标准诊断  2018-11-20 王林
        Integer deleteShow = DiagnosisVO.DELETE_LINE_NO_SHOW;
        RecomUserInfoBO userInfo = remoter.getUserInfo(doctorId, null, true);
        if (null == userInfo || null == userInfo.getDoctor()) {
            LOGGER.warn("[诊断列表] repeat error ; 用户信息异常 : doctorId={}", doctorId);
            throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_EXITS);
        }
        RecomDoctorBO doctorBO = userInfo.getDoctor();
        //表示医生是灰度诊断医生,删除非标准诊断
        if (doctorBO.getLabel() != null && doctorBO.getLabel().getDiagnosis()) {
            deleteShow = DiagnosisVO.DELETE_LINE_SHOW;
            commonDiagnosisVO.setStandard(CommonDiagnosisVO.STANDARD_YES);
        }

        if (!standardDiag.isEmpty()) {
            List<DrStandardDiagnosis> standardDiagList = drStandardDiagnosisRepository.findByIdIn(standardDiag);
            if (!standardDiagList.isEmpty()) {
                for (DrStandardDiagnosis diagnosis : standardDiagList) {
                    DiagnosisVO vo = new DiagnosisVO();
                    Long id = diagnosis.getId();
                    vo.setId(id);
                    vo.setName(diagnosis.getName());
                    vo.setType(DiagnosisVO.TYPE_STANDARD);
                    vo.setDeleteLineShow(DiagnosisVO.DELETE_LINE_NO_SHOW);
                    map.put(id + "-" + DiagnosisVO.TYPE_STANDARD, vo);
                }
            }
        }

        List<DiagnosisVO> list = new ArrayList<>(100);
        for (Entry<String, DiagnosisVO> entry : map.entrySet()) {
            String key = entry.getKey();
            DiagnosisVO value = map.get(key);
            if (value != null) {
                list.add(value);
            }
        }
        List<ABCVO<DiagnosisVO>> diagnosisList = ABCUtil.toABC(list, "name", DiagnosisVO.class);
        commonDiagnosisVO.setList(diagnosisList);
        return commonDiagnosisVO;
    }

    /**
     * 获取标准诊断
     *
     * @param diagnosisList
     *
     * @return
     *
     * @throws BusinessException
     */

    @Override
    public RecomDiagnosisBO getStandardDiagnosis(List<String> list) throws BusinessException {
        RecomDiagnosisBO recomDiagnosisBO = new RecomDiagnosisBO();
        List<DrStandardDiagnosis> drStandardDiagnoses = drStandardDiagnosisRepository.findByNameIn(list);
        List<String> diagnosisList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(drStandardDiagnoses)) {
            if (drStandardDiagnoses.size() != list.size()) {
                recomDiagnosisBO.setDiagnoseDeleteTip(RECOM_DELETE_DIAGNOSE_TIP);
            }
            for (DrStandardDiagnosis tempDiagnosis : drStandardDiagnoses) {
                diagnosisList.add(tempDiagnosis.getName());
            }
        } else {
            recomDiagnosisBO.setDiagnoseDeleteTip(RECOM_DELETE_DIAGNOSE_TIP);
        }
        recomDiagnosisBO.setDiagnosis(diagnosisList);
        return recomDiagnosisBO;
    }

    @Override
    public List<SuperviseDiagnosisBO> diagnosiss(List<String> names) {
        List<DrStandardDiagnosis> diagnoses = drStandardDiagnosisRepository.findByNameIn(names);
        if (CollectionUtil.isNotEmpty(diagnoses)) {
            return diagnoses.stream()
                            .map(diagnos -> SuperviseDiagnosisBO.builder()
                                                                .id(diagnos.getId())
                                                                .code(diagnos.getMainCode())
                                                                .name(diagnos.getName())
                                                                .build())
                            .collect(Collectors
                                    .toList());
        }
        return new ArrayList<>();
    }
}


